# ================================================================================
# Copyright (c) 2008, Earlybird Technology Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of Earlybird Technology Inc. nor the
#       names of its contributors may be used to endorse or promote products
#       derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY Earlybird Technology Inc. ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Earlybird Technology Inc. BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ================================================================================

# Pythons Libraries
import datetime
import time
import sys

# Google API's and Extensions
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.api import users

# Import Datastore Model
from create_punch import Punch
from create_project import Project

# Exception Classes
class DataInputError(): pass

class Report(webapp.RequestHandler):
    # This method will have query information passed to it in the URL. It will return the results in xml.
    # The information expected is as follows
    #    - 'employee' as an emil address
    #    - 'project' as the display name of a project
    #    - 'earliest' as a date in the form 'YYYY-MM-DD HH:MM:SS'
    #    - 'latest' as a date in the form 'YYYY-MM-DD HH:MM:SS' that occures after 'earliest'
    #    - 'count_limit' as an integer greater than 0 (the number of results to be returned). No more than 1000 results will be returned.
    # Any of these properties can have a '*' value which will eliminate it from the query's filter. 
    def get(self):

        # Initialize flags
        # I do not like all these flags, but no alternative obviously presents itself...
        filter_by_employee = True
        filter_by_project = True
        filter_by_earliest = True
        filter_by_latest = True
        limit_by_count = True


        # Parse and validate all POST data
        try:
            # Find employee to filter by
            # Check for keywords
            if self.request.get('employee') == '*':
                filter_by_employee = False
            elif self.request.get('employee') == 'current':
                employee_filter = users.GetCurrentUser()
            else:
                # Retrieve a UserProperty object with the given email address.
                # An error will not be raised if the user does not exist.
                employee_filter = users.User(self.request.get('employee'))

            
            # Find project to filter by
            # Check for keywords
            if self.request.get('project') == '*':
                filter_by_project = False
            else:
                # Find the project's key
                project_query = Project.all()
                project_query.filter('display_name =', self.request.get('project'))  # Filter by display name
                project_results = project_query.get()                   # Retrieve one result
                if project_results == None:                             # Project does not exist
                    raise DataInputError                                #todo: include information about offending parameter
                else:
                    project_filter = project_results.key()              # Retrieve the project's key


            # Find Earliest to filter by
            # Check for keywords
            if self.request.get('earliest') == '*':
                filter_by_earliest = False
            else:
                # result is a datetime object, the same that google uses for its DateTimeProperty
                earliest = string_to_datetime_obj(self.request.get('earliest'))

            
            # Find Latest date filter by
            # Check for keywords
            if self.request.get('latest') == '*':
                filter_by_latest = False
            else:
                latest = string_to_datetime_obj(self.request.get('latest'))
        

            # Find maximum number of results to return
            # Check for keywords
            if self.request.get('count_limit') == '*':
                limit_by_count = False
            # Validate: 0 < suggested_query_limit < 1000 
            elif int(self.request.get('count_limit')) > 1000:
                query_limit = 1000
            elif int(self.request.get('count_limit')) < 0:
                raise DataInputError
            # Assign limit for filter
            else:
                query_limit = int(self.request.get('count_limit'))
            
            # only test if both are assigned a value
            if filter_by_earliest & filter_by_latest:
                if earliest >= latest:
                    raise DataInputError


            # Google Query Interface
            # All data has been validated by this point
            # Note: if any data was invalid, the try loop would have beed exited with an exception
            punch_query = Punch.all()
            if filter_by_employee:
                punch_query.filter('employee =', employee_filter)
            if filter_by_project:
                punch_query.filter('project =', project_filter)
            if filter_by_earliest:
                punch_query.filter('punch_date >', earliest)
            if filter_by_latest:
                punch_query.filter('punch_date <', latest)
            punch_query.order('-punch_date')
            if limit_by_count:
                punch_results = punch_query.fetch(query_limit)
            else:
                punch_results = punch_query.fetch(1000)  # Maximum allowed by datastore
        
            if punch_results == None:
                self.response.headers['Content-Type'] = 'text/plain'
                self.response.out.write('No Results')
            else:    
                # Build XML response
                self.response.headers['Content-Type'] = 'text/xml'
                self.response.out.write('<results>')
                for result in punch_results:
                    self.response.out.write('<punch>')
                    self.response.out.write('<employee>' + result.employee.email() + '</employee>')
                    self.response.out.write('<date>' + str(result.punch_date) + '</date>')
                    self.response.out.write('<hours>' + str(result.hours) + '</hours>')
                    self.response.out.write('<project>' + result.project.display_name + '</project>')
                    self.response.out.write('<summary>' + result.summary + '</summary>')
                    self.response.out.write('</punch>')
                self.response.out.write('</results>')
                
        except (users.UserNotFoundError,    # User's email adress was not properly formatted (will not be raised if user does not exist)
                DataInputError,             # Generic Exception
                ValueError,                  # Date string was not formatted properly
                ):    
            self.response.clear()
            query_string = self.request.query_string
            self.response.out.write('''
                <html>
                    <body>
                        <h1>Oops... Something Went Wrong.</h1>
                        <a href="/static/report.html">Give it another shot.</a>
                    </body>
                </html>
                ''')
    
    
def string_to_datetime_obj(date_string):
    datetime_object = datetime.datetime(*(time.strptime(date_string, "%Y-%m-%d %H:%M:%S")[0:6]))
    # If string was invalid, ValueError will be thrown
    return datetime_object
